/******************************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Ltd.                       */
/*                                                                            */
/*  NAME      = postgresql_resposne.c                                         */
/*  NOTE      = postgresql请求解析                                            */
/*  DATE      = 2016/01/06 by zhengxd                                         */
/******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <stdint.h>
#include <limits.h>

#include "postgresql_core.h"
#include "postgresql_comm.h"

extern void *g_p_apm_context;

#define POSTGRESQL_AUTHENTICATION_OK                   0
#define POSTGRESQL_AUTHENTICATION_KERBEROSV4           1
#define POSTGRESQL_AUTHENTICATION_KERBEROSV5           2
#define POSTGRESQL_AUTHENTICATION_CLEARTEXTPASSWORD    3
#define POSTGRESQL_AUTHENTICATION_CRYPTPASSWORD        4
#define POSTGRESQL_AUTHENTICATION_MD5PASSWORD          5
#define POSTGRESQL_AUTHENTICATION_SCMCREDENTIAL        6
#define POSTGRESQL_AUTHENTICATION_GSS                  7
#define POSTGRESQL_AUTHENTICATION_SSPI                 9
#define POSTGRESQL_AUTHENTICATION_GSSCONTINUE          8

#define POSTGRESQL_LOGIN_START                         0
#define POSTGRESQL_LOGIN_SUCC                          1
#define POSTGRESQL_LOGIN_REJ                           2

/*******************************************************************************
*  FUNC     :  上报response数据
*  ARGS     :  p_st_conn (链接信息)
*           :  p_st_private (私有信息)
*           :  p_st_hash_report (hash)
*  RTN      :
*  NOTE     :  p_st_private肯定不为NULL
*******************************************************************************/
FR_STATIC int __postgresql_report_resp(Conntrack *p_st_conn
                                    , L7session_postgresql_private *p_st_private
                                    , Frhash_header *p_st_hash_report)
{
    int i_ret = FR_SUCC;
    Frreport_body_data *p_st_report_body = NULL;
    L7_req_postgresql *p_st_req = p_st_private->p_st_req;
    Postgresql_req_cmd *p_st_command = NULL;
    Frlist_node *p_hash_node = NULL;
    Frlist_node *p_st_node = NULL;

    p_st_node = FRLIST_FIRST_NODE(&p_st_req->st_command_lst);
    while (p_st_node)
    {
        p_st_command = (Postgresql_req_cmd *)FRLIST_GET_NODE_DATA(p_st_node);

        if (p_st_command->i_report_flg == FR_YES)
        {
            /* 创建report body */
            p_st_report_body = postgresql_new_report_body(p_st_conn, p_st_private->p_st_sess);
            /* 创建report body失败时，不释放做成的request */
            if (p_st_report_body == NULL)
            {
                frdbg("call postgresql_new_report_body failed!\n");
                return APM_L7_ANALYZE_FAIL;
            }

            /* 上报request数据 */
            i_ret = frdb_report_resp2(p_st_conn, p_st_hash_report
                                     , p_st_command->p_st_statement
                                     , p_st_command->p_st_resultset
                                     , p_st_command->l_err_code
                                     , p_st_command->p_err_msg
                                     , p_st_command->p_err_msg
                                     , &p_st_report_body);
            /* 上报request数据失败时，不释放做成的request */
            if (i_ret != FR_SUCC)
            {
                frdbg("frdb_report_req2 failed!\n");
                frreport_body_free(&p_st_report_body);
                return APM_L7_ANALYZE_SUCC;
            }
        }

        if (p_st_command->p_cursor_str
            && p_st_command->i_req_type == POSTGRESQL_TP_CLOSE)
        {
            /* 删除原有的 */
            frhash2_delStr(p_st_private->p_hash_prepare
                           , p_st_command->p_cursor_str
                           , strlen(p_st_command->p_cursor_str)
                           , frdb_statement_node_free);
        }

        if (p_st_command->p_cursor_str && p_st_command->i_new_flg == FR_YES)
        {
            /* 删除原有的 */
            frhash2_delStr(p_st_private->p_hash_prepare
                           , p_st_command->p_cursor_str
                           , strlen(p_st_command->p_cursor_str)
                           , frdb_statement_node_free);

            /* 创建hash节点 */
            p_hash_node = frlist_node_create(p_st_command->p_st_statement);
            if (p_hash_node == NULL)
            {
                return APM_L7_ANALYZE_FAIL;
            }
            /* 追加现有的 */
            i_ret = frhash2_addStr(p_st_private->p_hash_prepare
                                  , p_st_command->p_cursor_str
                                  , strlen(p_st_command->p_cursor_str)
                                  , p_hash_node);
            if (i_ret != FR_SUCC)
            {
                frfree(p_hash_node);
                return APM_L7_ANALYZE_FAIL;
            }
            p_st_command->i_new_flg = FR_NO;
        }

        p_st_node = FRLIST_NEXT_NODE(p_st_node);
    }

    return APM_L7_ANALYZE_SET_REQ(APM_L7_ANALYZE_SUCC);
}

/*******************************************************************************
*  FUNC:  解析error response
*  ARGS:  p_st_data (数据块)
*      :  i_packet_len(packet长度，包括长度位)
*      :  p_l_err_code(error code)
*      :  pp_err_msg(error message)
*  RTN :  APM_L7_ANALYZE_SUCC       (succ)
*         APM_L7_ANALYZE_TODO       (need more)
*         APM_L7_ANALYZE_FAIL       (fail)
*  NOTE:
*******************************************************************************/
FR_STATIC int __postgresql_parse_err_resp(Frbyte_Packet *p_st_data
                                       , int i_packet_len
                                       , long *p_l_err_code
                                       , char **pp_err_msg)
{
    int i_ret = FR_SUCC;
    uint8_t u8_format = 0;
    long l_err_code = 0;
    char *p_err_msg = NULL;
    char *p_err_code = NULL;
    int i_left_len = i_packet_len;

    if (bytebuff_remaining(p_st_data) < i_left_len)
    {
        frdbg("need more data!\n");
        return APM_L7_ANALYZE_TODO;
    }

    while (i_left_len)
    {
        i_ret = bytebuff_get_char(p_st_data, &u8_format);
        if (i_ret != FR_SUCC)
        {
            goto error;
        }
        i_left_len--;
        switch (u8_format)
        {
            case 0: //结束
                if (i_left_len != 0)
                {
                    goto error;
                }
                if (p_err_code)
                {
                    frfree(p_err_code);
                }
                *p_l_err_code = l_err_code;
                *pp_err_msg = p_err_msg;
                return APM_L7_ANALYZE_SUCC;

            case 'C': //error code
                if (p_err_code)
                {
                    frdbg("double error code!\n");
                    goto error;
                }
                i_ret = postgresql_get_string(p_st_data, &i_left_len, &p_err_code);
                if (i_ret != FR_SUCC)
                {
                    return APM_L7_ANALYZE_SUCC;
                }
                if (p_err_code)
                {
                    /* 转成数值 */
                    l_err_code = postgresql_err_code(p_err_code);
                    frfree(p_err_code);
                    p_err_code = NULL;
                }
                break;

            case 'M': //error message
                if (p_err_msg)
                {
                    frdbg("double error message!\n");
                    goto error;
                }
                i_ret = postgresql_get_string(p_st_data, &i_left_len, &p_err_msg);
                if (i_ret != FR_SUCC)
                {
                    return APM_L7_ANALYZE_SUCC;
                }
                break;

            default:
                i_ret = postgresql_skip_string(p_st_data, &i_left_len);
                if (i_ret != FR_SUCC)
                {
                    goto error;
                }
                break;
        }
    }

    goto error;

error:
    if (p_err_code)
    {
        frfree(p_err_code);
    }
    if (p_err_msg)
    {
        frfree(p_err_msg);
    }
    return APM_L7_ANALYZE_FAIL;
}


/*******************************************************************************
*  FUNC:  解析request
*  ARGS:  p_st_conn (入力链接信息)
*      :  p_st_hash_report(Frreport_data)
*      :  p_st_private(postgresql私有数据)
*  RTN :  参照module.h
*  NOTE:
*******************************************************************************/
int postgresql_parse_response(Conntrack *p_st_conn
                             , Frhash_header *p_st_hash_report
                             , L7session_postgresql_private *p_st_private)
{
    int i_ret = APM_L7_ANALYZE_SUCC;
    int i_end_flg = FR_NO;
    int i_packet_len = 0;
    uint8_t u8_type = 0;
    //uint32_t u32_len = 0;
    int i_left_len = 0;
    int i_login_flg = POSTGRESQL_LOGIN_START;
    long l_err_code = 0;
    unsigned long ul_rows = 0;
    uint32_t u32_auth_type = 0;
    char *p_result = NULL;
    char *p_tmp = NULL;
    char *p_err_msg = NULL;
    L7_req_postgresql *p_st_req = p_st_private->p_st_req;
    L7_resp_postgresql *p_st_resp = NULL;
    L7_fragment_postgresql *p_st_fragment = p_st_private->p_st_fragment;
    Frbyte_Packet *p_st_data = p_st_fragment->p_st_data;
    Postgresql_req_cmd *p_st_command = NULL;
    Frlist_node *p_st_node = NULL;
    Frreport_data *p_st_report_data=NULL;
    char arr_info[128] = {0};

    /* 在此之前没有接受到request信息, 忽略该response信息 */
    if (p_st_req == NULL)
    {
        frdbg("There no request before response!\n");
        return APM_L7_ANALYZE_FAIL;
    }

    bytebuff_setPosition(p_st_data, p_st_fragment->i_parsed_position);
    /* 先处理SSL/startup/cancel/password等请求 */
    switch (p_st_req->i_req_type)
    {
        case POSTGRESQL_TP_SSL_REQ:
        case POSTGRESQL_TP_CANCEL_REQ: //TODO
            return APM_L7_ANALYZE_SUCC;

        //case POSTGRESQL_TP_STARTUP:
        //    if (bytebuff_remaining(p_st_data) < 1 + 4 + 4)
        //    {
        //        frdbg("format incorrect!\n");
        //        return APM_L7_ANALYZE_FAIL;
        //    }
        //    i_ret = bytebuff_get_char(p_st_data, &u8_type);
        //    if (i_ret != FR_SUCC || u8_type != POSTGRESQL_TP_AUTHENTICATION)
        //    {
        //        frdbg("format incorrect!\n");
        //        return APM_L7_ANALYZE_FAIL;
        //    }
        //    /* 取得packet长度 */
        //    i_ret = bytebuff_get_int(p_st_data, 4, &u32_len, BYTE_BIG_ENDIAN);
        //    if (i_ret != FR_SUCC)
        //    {
        //        frdbg("Get packet length failed!\n");
        //        return APM_L7_ANALYZE_FAIL;
        //    }
        //    /* get auth type */
        //    i_ret = bytebuff_get_int(p_st_data, 4, &u32_auth_type, BYTE_BIG_ENDIAN);
        //    if (i_ret != FR_SUCC)
        //    {
        //        frdbg("Get auth type failed!\n");
        //        return APM_L7_ANALYZE_FAIL;
        //    }
        //    switch (u32_auth_type)
        //    {
        //        case POSTGRESQL_AUTHENTICATION_CLEARTEXTPASSWORD:
        //        case POSTGRESQL_AUTHENTICATION_CRYPTPASSWORD:
        //        case POSTGRESQL_AUTHENTICATION_MD5PASSWORD:
        //            return APM_L7_ANALYZE_SUCC;
        //
        //        default:
        //            frdbg("format incorrect!\n");
        //            return APM_L7_ANALYZE_FAIL;
        //    }

        case POSTGRESQL_TP_STARTUP:
        case POSTGRESQL_TP_PASSWORD_MSG:
            while (bytebuff_remaining(p_st_data))
            {
                if (bytebuff_remaining(p_st_data) < 5)
                {
                    frdbg("need more data!\n");
                    return APM_L7_ANALYZE_TODO;
                }
                i_ret = bytebuff_get_char(p_st_data, &u8_type);
                if (i_ret != FR_SUCC)
                {
                    frdbg("get packet type failed!\n");
                    return APM_L7_ANALYZE_FAIL;
                }
                /* 取得packet长度 */
                i_ret = postgresql_get_int2(p_st_data, &i_packet_len);
                if (i_ret != FR_SUCC || i_packet_len < 4)
                {
                    frdbg("Get packet length failed!\n");
                    return APM_L7_ANALYZE_FAIL;
                }
                i_packet_len -= 4;
                if (bytebuff_remaining(p_st_data) < i_packet_len)
                {
                    frdbg("need more data!\n");
                    return APM_L7_ANALYZE_TODO;
                }

                switch (u8_type)
                {
                    case POSTGRESQL_TP_AUTHENTICATION:
                        if (i_packet_len < 4)
                        {
                            frdbg("Get packet length failed!\n");
                            return APM_L7_ANALYZE_FAIL;
                        }
                        /* get auth type */
                        i_ret = bytebuff_get_int(p_st_data, 4, &u32_auth_type
                                                , BYTE_BIG_ENDIAN);
                        if (i_ret != FR_SUCC)
                        {
                            frdbg("Get auth type failed!\n");
                            return APM_L7_ANALYZE_FAIL;
                        }
                        /* connect 成功 */
                        if (u32_auth_type == POSTGRESQL_AUTHENTICATION_OK)
                        {
                            p_st_private->p_st_sess->st_login = p_st_fragment->st_end_timestamp;
                            i_login_flg = POSTGRESQL_LOGIN_SUCC;
                        }
                        bytebuff_skip(p_st_data, i_packet_len - 4);
                        break;

                    case POSTGRESQL_TP_READY_QUERY:
                        if (i_packet_len != 1 || bytebuff_remaining(p_st_data) != 1)
                        {
                            return APM_L7_ANALYZE_FAIL;
                        }
                        if (i_login_flg == POSTGRESQL_LOGIN_SUCC)
                        {
                            if (p_st_private->p_st_sess->p_db_user)
                            {
                                snprintf(arr_info, sizeof(arr_info), "%s login", p_st_private->p_st_sess->p_db_user);
                            }
                            else
                            {
                                snprintf(arr_info, sizeof(arr_info), "login");
                            }
                            p_st_report_data = frreport_update_create(p_st_conn
                                                , APM_REPORT_UPDATE_LOGON
                                                , arr_info, &p_st_fragment->st_end_timestamp);
                            if (p_st_report_data)
                            {
                                i_ret = frreport_send(g_p_apm_context, &p_st_report_data);
                                if (i_ret != FR_SUCC)
                                {
                                    return APM_L7_ANALYZE_FAIL;
                                }
                            }
                        }
                        return APM_L7_ANALYZE_SUCC;

                    case POSTGRESQL_TP_ERROR_RESP:
                        bytebuff_skip(p_st_data, i_packet_len);
                        if (bytebuff_remaining(p_st_data))
                        {
                            return APM_L7_ANALYZE_FAIL;
                        }
                        if (p_st_private->p_st_sess->p_db_user)
                        {
                            snprintf(arr_info, sizeof(arr_info), "%s login", p_st_private->p_st_sess->p_db_user);
                        }
                        else
                        {
                            snprintf(arr_info, sizeof(arr_info), "login");
                        }
                        p_st_report_data = frreport_update_create(p_st_conn
                                            , APM_REPORT_UPDATE_LOGON_REJECT
                                            , arr_info, &p_st_fragment->st_end_timestamp);
                        if (p_st_report_data)
                        {
                            i_ret = frreport_send(g_p_apm_context, &p_st_report_data);
                            if (i_ret != FR_SUCC)
                            {
                                return APM_L7_ANALYZE_FAIL;
                            }
                        }
                        return APM_L7_ANALYZE_SUCC;

                    default:
                        bytebuff_skip(p_st_data, i_packet_len);
                        break;
                }
            }
            return APM_L7_ANALYZE_TODO;

        default:
            break;
    }

    if (p_st_private->p_st_resp == NULL)
    {
        /* 创建response信息 */
        p_st_private->p_st_resp = (L7_resp_postgresql *)frmalloc(sizeof(L7_resp_postgresql));
        if (p_st_private->p_st_resp == NULL)
        {
            frdbg("create response failed!\n");
            return APM_L7_ANALYZE_FAIL;
        }
        memset(p_st_private->p_st_resp, 0x0, sizeof(L7_resp_postgresql));
        p_st_private->p_st_resp->st_start_timestamp = p_st_fragment->st_start_timestamp;
        p_st_private->p_st_resp->st_end_timestamp = p_st_fragment->st_end_timestamp;
    }
    p_st_resp = p_st_private->p_st_resp;

    /* 取得command */
    if (p_st_resp->p_st_node == NULL)
    {
        p_st_resp->p_st_node = FRLIST_FIRST_NODE(&p_st_req->st_command_lst);
        if (p_st_resp->p_st_node)
        {
            p_st_node = p_st_resp->p_st_node;
            p_st_command = (Postgresql_req_cmd *)FRLIST_GET_NODE_DATA(p_st_node);
        }
    }
    else
    {
        p_st_node = p_st_resp->p_st_node;
        p_st_command = (Postgresql_req_cmd *)FRLIST_GET_NODE_DATA(p_st_node);
    }

    /* 解析response */
    while (bytebuff_remaining(p_st_data) >= 5)
    {
        if (i_end_flg == FR_YES)
        {
            frdbg("format incorrect\n");
            return APM_L7_ANALYZE_FAIL;
        }
        /* 取得packet type */
        i_ret = bytebuff_get_char(p_st_data, &u8_type);
        if (i_ret != FR_SUCC)
        {
            frdbg("get packet type failed!\n");
            return APM_L7_ANALYZE_FAIL;
        }
        /* 取得packet长度 */
        i_ret = postgresql_get_int2(p_st_data, &i_packet_len);
        if (i_ret != FR_SUCC || i_packet_len < 4)
        {
            frdbg("Get packet length failed!\n");
            return APM_L7_ANALYZE_FAIL;
        }

        /*
        *  由于packet长度只是包括自身长度，但是不包括packet type长度，
        *  所以实际长度是取得的packet长度加1
        */
        i_packet_len += 1;
        i_left_len = i_packet_len - 5;
        p_st_resp->i_packet_len += i_packet_len;

        switch (u8_type)
        {
            case POSTGRESQL_TP_COMMAND_COMPLETE:
                p_st_resp->st_end_timestamp = p_st_fragment->st_end_timestamp;
                if (bytebuff_remaining(p_st_data) < i_left_len)
                {
                    frdbg("need more data!\n");
                    return APM_L7_ANALYZE_TODO;
                }
                i_ret = postgresql_get_string(p_st_data, &i_left_len, &p_result);
                if (i_ret != FR_SUCC || p_result == NULL || i_left_len != 0)
                {
                    frdbg("Get response rows string failed!\n");
                    return APM_L7_ANALYZE_FAIL;
                }
                /* 找到最后一个空格 */
                p_tmp = strrchr(p_result, ' ');
                if (p_tmp == NULL)
                {
                    frfree(p_result);
                    ul_rows = 0;
                }
                else
                {
                    /* 转成数值 */
                    i_ret = postgresql_atol(p_tmp + 1, &ul_rows);
                    frfree(p_result);
                    if (i_ret != FR_SUCC)
                    {
                        ul_rows = 0;
                    }
                }

                /* set result set */
                if (p_st_command)
                {
                    if (FRLIST_NEXT_NODE(p_st_node))
                    {
                        i_ret = frdb_set_resultset_resp2(p_st_command->p_st_resultset
                                                        , p_st_resp->i_packet_len
                                                        , 1
                                                        , &ul_rows
                                                        , &(p_st_resp->st_start_timestamp)
                                                        , &(p_st_resp->st_end_timestamp));
                        if (i_ret != FR_SUCC)
                        {
                            frdbg("set resultset failed!\n");
                            return APM_L7_ANALYZE_FAIL;
                        }
                        /* get next */
                        p_st_resp->p_st_node = FRLIST_NEXT_NODE(p_st_node);
                        p_st_node = p_st_resp->p_st_node;
                        p_st_command = (Postgresql_req_cmd *)FRLIST_GET_NODE_DATA(p_st_node);
                        /* init */
                        p_st_resp->i_packet_len = 0;
                        p_st_resp->st_start_timestamp = p_st_fragment->st_end_timestamp;
                        p_st_resp->st_end_timestamp = p_st_fragment->st_end_timestamp;
                    }
                    /* 为了把最后的READY QUERY也计算进来 */
                    else
                    {
                        p_st_resp->ul_rows = ul_rows;
                    }
                }
                break;

            case POSTGRESQL_TP_ERROR_RESP:
                p_st_resp->st_end_timestamp = p_st_fragment->st_end_timestamp;
                i_ret = __postgresql_parse_err_resp(p_st_data, i_left_len
                                                   , &l_err_code, &p_err_msg);
                if (i_ret != APM_L7_ANALYZE_SUCC)
                {
                    frdbg("parse error response failed!\n");
                    return i_ret;
                }
                p_st_command->l_err_code = l_err_code;
                if (p_st_command->p_err_msg)
                {
                    frfree(p_st_command->p_err_msg);
                }
                p_st_command->p_err_msg = p_err_msg;
                p_err_msg = NULL;

                /* set result set */
                if (p_st_command)
                {
                    if (FRLIST_NEXT_NODE(p_st_node))
                    {
                        i_ret = frdb_set_resultset_resp2(p_st_command->p_st_resultset
                                                        , p_st_resp->i_packet_len
                                                        , 0
                                                        , NULL
                                                        , &(p_st_resp->st_start_timestamp)
                                                        , &(p_st_resp->st_end_timestamp));
                        if (i_ret != FR_SUCC)
                        {
                            frdbg("set resultset failed!\n");
                            return APM_L7_ANALYZE_FAIL;
                        }

                        /* get next */
                        p_st_resp->p_st_node = FRLIST_NEXT_NODE(p_st_node);
                        p_st_node = p_st_resp->p_st_node;
                        p_st_command = (Postgresql_req_cmd *)FRLIST_GET_NODE_DATA(p_st_node);
                        /* init */
                        p_st_resp->i_packet_len = 0;
                        p_st_resp->st_start_timestamp = p_st_fragment->st_end_timestamp;
                        p_st_resp->st_end_timestamp = p_st_fragment->st_end_timestamp;
                    }
                }
                break;

             case POSTGRESQL_TP_READY_QUERY:
                if (bytebuff_remaining(p_st_data) < i_left_len)
                {
                    bytebuff_set_skip(p_st_data, i_left_len - bytebuff_remaining(p_st_data));
                    return APM_L7_ANALYZE_TODO;
                }
                bytebuff_skip(p_st_data, i_left_len);
                p_st_resp->st_end_timestamp = p_st_fragment->st_end_timestamp;
                i_end_flg = FR_YES;
                /* set result set */
                if (p_st_command)
                {
                    i_ret = frdb_set_resultset_resp2(p_st_command->p_st_resultset
                                                    , p_st_resp->i_packet_len
                                                    , 1
                                                    , &p_st_resp->ul_rows
                                                    , &(p_st_resp->st_start_timestamp)
                                                    , &(p_st_resp->st_end_timestamp));
                    if (i_ret != FR_SUCC)
                    {
                        frdbg("set resultset failed!\n");
                        return APM_L7_ANALYZE_FAIL;
                    }
                }
                break;

            default:
                if (bytebuff_remaining(p_st_data) < i_left_len)
                {
                    bytebuff_set_skip(p_st_data, i_left_len - bytebuff_remaining(p_st_data));
                    return APM_L7_ANALYZE_TODO;
                }
                bytebuff_skip(p_st_data, i_left_len);
                break;
        }
        p_st_fragment->i_parsed_position = bytebuff_getPosition(p_st_data);
    }

    if (i_end_flg == FR_YES)
    {
        //report resposne
        i_ret = __postgresql_report_resp(p_st_conn
                                        , p_st_private
                                        , p_st_hash_report);
        return i_ret;
    }
    else
    {
        return APM_L7_ANALYZE_TODO;
    }
}
